fix: oversubscription perps top movers#33150
Conversation
PR template — items to address before "Ready for review"Warnings — informational, address before merging:
See docs/readme/ready-for-review.md for the full Definition of Ready for Review. |
🧪 Flaky unit test detectionRun history flaky detectionHistorical failure rate is a hint, not proof — review each suggestion in context. See the flaky-test-detection skill for the full pattern reference and manual audit workflow. Failures / runs sampled per window:
AI-detected flaky patterns
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #33150 +/- ##
==========================================
+ Coverage 84.31% 84.32% +0.01%
==========================================
Files 6080 6084 +4
Lines 162378 162522 +144
Branches 39577 39616 +39
==========================================
+ Hits 136905 137050 +145
+ Misses 16021 16010 -11
- Partials 9452 9462 +10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…taMask/metamask-mobile into fix/oversubscription-perps-top-movers
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9f36d83. Configure here.
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
These changes directly affect:
The changes are well-contained to the TrendingView/Perps area with no impact on core wallet infrastructure, navigation, or other flows. Risk is medium because the WebSocket subscription management changes could affect live data display in the Perps section. Performance Test Selection: |
|
⚡ Performance Test Results
✅ All tests passed · 2 tests · 1 device 📱 Devices tested (1)Android: Google Pixel 8 Pro (v14.0) ✅ Passed Tests (2)
Branch: |
geositta
left a comment
There was a problem hiding this comment.
Nice work overall addressing the root cause. Before approving, please fix the stale market data path. I also recommend narrowing the active tab update so this performance change does not rerender every loaded Explore feed.
| previous && | ||
| previous.market.change24hPercent === market.change24hPercent | ||
| ) { | ||
| return previous; |
There was a problem hiding this comment.
Please use the updated feed item when items changes. If a REST refresh changes maxLeverage or another market field while change24hPercent stays the same, this branch keeps the old market data. The claimed render reduction also does not occur at the current call site because PerpsPillItem receives a new onCardPress function on each render, causing React.memo to render it again.
Please retain old items only for WebSocket updates where the complete feed item is unchanged, and add a test where the percentage remains constant while another market field changes.
| ...(source ? { source } : {}), | ||
| }); | ||
| previousTabRef.current = destinationTab; | ||
| setActiveTab(destinationTab); |
There was a problem hiding this comment.
This state update rerenders ExploreFeed on every tab switch. useExploreRefresh() returns a new object, which recreates all six tab elements and causes every loaded feed to rerender - not only the component using the context. This adds rendering work during tab changes despite the context being intended to isolate active tab updates. Please move the active tab state into a smaller component or preserve stable tab content references when the active tab changes.




Description
Problem
The Now-tab perps movers strip subscribed to live prices for all ~329 markets and, on every ~3s push, rebuilt/re-sorted the full set and re-rendered — just to show the top 12 pills. Since Explore tabs never unmount, this kept running even on other tabs or after leaving Explore.
Why we keep the full-market subscription
Subscribing to only the 12 displayed symbols would break ranking: a market outside the top 12 can move into it, and we'd miss that price change. Correct live re-ordering requires watching every market.
The middle ground
The WebSocket already pushes all markets regardless of subscription — the real cost was the unconditional
setState, the per-tick re-sort, and new object identities per tick. The newusePerpsLiveMovershook merges every tick into a ref (no render per tick) and derives the displayed top-12 synchronously in render, so base-data loads and gainers/losers toggles reflect immediately with no empty frame. Live ticks only trigger a render when the displayed top-12 actually changes (via asymbol:formattedPercentfingerprint), reusing prior item references so unchanged pills skip re-render.The full rank/filter/sort pass itself is also throttled to a configurable interval (
recomputeIntervalMs, 10s on the Now tab): ticks arriving inside the interval accumulate losslessly in the ref, and at most one ranking pass runs per interval using the freshest data. Idle ticks cost only a cheap ref merge. On (re)subscribe — mount, tab resume, symbol-set change — the throttle anchor resets so the stream's cached snapshot surfaces promptly instead of waiting out a full interval.On top of that, the subscription pauses entirely when not visible: an
ExploreActiveTabContexttellsPerpsBlockwhether the Now tab is active, combined withuseIsFocused()for leaving Explore. The same focus gating is applied to the What's Happening detail view's perps price hook. Disabling freezes the last-known data rather than clearing it, so nothing blanks out and it refreshes immediately on resume.Changelog
CHANGELOG entry: improve performance on Perps Movers in explore
Related issues
Fixes:
Manual testing steps
Screenshots/Recordings
Before
After
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist
Note
Low Risk
Performance-focused UI changes with broad unit test coverage; no auth, payments, or data-persistence changes. Residual risk is subtle ranking or stale UI if gating/fingerprint logic regresses.
Overview
Reduces Explore perps movers work by replacing inline
usePerpsLivePrices+ full-list sort inNowTab’sPerpsBlockwith a newusePerpsLiveMovershook that still watches all markets for correct top-12 ranking, but stores ticks in a ref and only re-renders when the visible movers fingerprint changes (with optional 10s batching, stable item references, andenabledto tear down the subscription and freeze the last slice).Pauses subscriptions when not visible:
ExploreActiveTabContext/useExploreActiveTabexpose the active Explore tab without prop-drilling through every mounted tab;TrendingViewtracksactiveTabon tab change and wrapsTabsList.PerpsBlockenables live movers only when the screen is focused and the Now tab is active.useWhatsHappeningAssetPricespasses an empty symbol list while unfocused so perps prices pause without clearing displayed values.Tests cover the new hook, context, tab gating, and focus behavior.
Reviewed by Cursor Bugbot for commit ee983bd. Bugbot is set up for automated code reviews on this repo. Configure here.